<?php
/**
 * Created by PhpStorm.
 * User: meng
 * Date: 2023/12/21
 * Time: 10:40
 */

namespace app\admin\service;

use lib\common\CommonService;
use lib\exception\FailureException;
use lib\exception\ParamsException;
use lib\utils\CacheUtils;
use lib\utils\TreeDataUtils;
use model\SettingItemModel;
use model\SettingModel;
use Webman\Config;

class SettingsService extends CommonService
{
    private SettingModel $settingModel;
    private SettingItemModel $settingItemModel;

    const SETTING_ITEM_TYPE = ['radio', 'checkbox', 'select', 'select:multiple', 'input:text', 'input:number', 'textarea'];
    const SETTING_ITEM_TYPE_WITH_OPTIONS = ['radio', 'checkbox', 'select', 'select:multiple'];

    const CACHE_VALUE_KEY = 'sys_settings';

    private string $cacheKey;

    /**
     * @param SettingModel $settingModel
     * @param SettingItemModel $settingItemModel
     */
    public function __construct (SettingModel $settingModel, SettingItemModel $settingItemModel) {
        $this->settingModel = $settingModel;
        $this->settingItemModel = $settingItemModel;
        $this->cacheKey = Config::get('laravelcache.prefix') . self::CACHE_VALUE_KEY;
        parent::__construct();
    }


    public function settingList (): array {
        $data = $this->settingModel->order(['list_order' => 'DESC', 'id' => 'ASC'])->select()->toArray();
        $tree = new TreeDataUtils($data, 'parentid', 'id', 'children', []);
        return $tree->getTree();
    }

    public function createSetting ($params): void {
        $this->settingModel::create($params, $this->settingModel::ALLOWED_FIELDS);
        $this->clearCache();
    }

    public function updateSetting ($id, $params): void {
        $setting = $this->findSetting($id);
        $setting->allowField($this->settingModel::ALLOWED_FIELDS)->save($params);
        $this->clearCache();
    }

    public function deleteSetting ($id): void {
        $setting = $this->findSetting($id);
        if ($setting['parentid'] > 0) {
            $settingItem = $this->settingItemModel->where('setting_id', $id)->find();
            if (!empty($settingItem)) {
                throw new ParamsException('此类型下存在配置项, 无法删除');
            }
        } else {
            if ($this->settingModel->where('parentid', $id)->count() > 0) {
                throw new ParamsException('此类型下存在子配置, 无法删除');
            }
        }
        $setting->delete();
        $this->clearCache();
    }

    public function createSettingItem ($id, $params): void {
        $setting = $this->findSetting($id);
        if ($setting['parentid'] == 0) {
            throw new FailureException('顶级配置分类下不能添加配置项');
        }
        $settingItem = $this->settingItemModel->where('key', $params['key'])->find();
        if (!empty($settingItem)) {
            throw new FailureException('配置标识重复');
        }
        if (in_array($params['type'], self::SETTING_ITEM_TYPE_WITH_OPTIONS)) {
            $params['options'] = cms_json_encode($params['options']);
        } else {
            unset($params['options']);
        }
        $params['setting_id'] = $id;
        $this->settingItemModel::create($params, $this->settingItemModel::ALLOWED_FIELDS);
        $this->clearCache();
    }

    public function updateSettingItem ($id, $itemId, $params): void {
        $setting = $this->findSetting($id);
        if ($setting['parentid'] == 0) {
            throw new FailureException('顶级配置分类下不能添加配置项');
        }
        $settingItem = $this->settingItemModel->where('key', $params['key'])->where('id', '<>', $itemId)->find();
        if (!empty($settingItem)) {
            throw new FailureException('配置标识重复');
        }
        if (in_array($params['type'], self::SETTING_ITEM_TYPE_WITH_OPTIONS)) {
            $params['options'] = cms_json_encode($params['options']);
        } else {
            unset($params['options']);
        }
        $settingItem = $this->settingItemModel->where('id', $itemId)->find();
        if (empty($settingItem)) {
            throw new FailureException('配置项不存在');
        }
        $settingItem->allowField($this->settingItemModel::ALLOWED_UPLOAD_FIELDS)->save($params);
        $this->clearCache();
    }

    public function deleteSettingItem ($id, $itemId): void {
        $setting = $this->findSetting($id);
        $settingItem = $this->settingItemModel->where('id', $itemId)->where('setting_id', $setting['id'])->find();
        if (empty($settingItem)) {
            throw new FailureException('配置项不存在');
        }
        $settingItem->delete();
        $this->clearCache();
    }

    public function findSetting ($id) {
        $setting = $this->settingModel->find($id);
        if (empty($setting)) {
            throw new FailureException('配置不存在');
        }
        return $setting;
    }

    public function settingItemList ($id): array {
        $setting = $this->findSetting($id);
        return $this->settingItemModel
            ->json(['options'], true)
            ->withAttr('value', function ($value, $data) {
                if (in_array($data['type'], ['checkbox', 'select:multiple'])) {
                    if (empty($value)) {
                        return [];
                    }
                    return json_decode($value, true);
                }
                return empty($value) ? '' : json_decode($value, true);
            })
            ->where('setting_id', $setting['id'])
            ->order(['id' => 'ASC'])
            ->select()->toArray();
    }

    public function saveSettingValue ($params): void {
        $keys = array_keys($params);
        $items = $this->settingItemModel->where('key', 'in', $keys)->select()->toArray();
        if (!empty($items)) {
            foreach ($items as $item) {
                $this->settingItemModel->where('id', $item['id'])->update([
                    'value' => cms_json_encode($params[$item['key']])
                ]);
            }
            $this->clearCache();
        }
    }

    public function clearCache (): void {
        CacheUtils::forget($this->cacheKey);
    }

    public function settingValue (string|array $key, $defaultValue = null) {
        if (is_string($key)) {
            $value = CacheUtils::hGet($this->cacheKey, $key);
            if (false === $value) {
                $value = $this->settingItemModel->where('key', $key)->value('value');
                if (!is_null($value)) {
                    CacheUtils::hSet($this->cacheKey, $key, $value);
                    return json_decode($value, true);
                }
                return $defaultValue;
            }
            return is_null($value) ? $defaultValue : json_decode($value);
        } else if (is_array($key)) {
            $values = CacheUtils::hMGet($this->cacheKey, $key);
            $result = [];
            $unCached = [];
            foreach ($values as $index => $value) {
                if (false === $value) {
                    $unCached[] = $key[$index];
                }
                $result[$key[$index]] = isset($value) ? json_decode($value) : $value;
            }
            if (!empty($unCached)) {
                $storege = $this->settingItemModel->where('key', 'in', $unCached)->column('value', 'key');
                if (!empty($storege)) {
                    CacheUtils::hMSet($this->cacheKey, $storege);
                }
            }
            foreach ($result as $key => $item) {
                if (false === $item) {
                    $result[$key] = !isset($storege[$key]) ? $defaultValue : json_decode($storege[$key], true);
                }
            }
            return $result;
        }
        return $defaultValue;
    }
}
